Skip to content

docs(skills): 拒收类用例最低断言 code + status,不是「它抛了」—— 三份 os-dev / pm-dispatch 文本同步落条款 (#6144) - #6233

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-6144-rejection-assert-envelope
Aug 7, 2026
Merged

docs(skills): 拒收类用例最低断言 code + status,不是「它抛了」—— 三份 os-dev / pm-dispatch 文本同步落条款 (#6144)#6233
hotlong merged 1 commit into
mainfrom
claude/issue-6144-rejection-assert-envelope

Conversation

@hotlong

@hotlong hotlong commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Fixes #6144

#6144 的定稿口径(PM 认领评论 5216248198)与分诊评论 5212181851 的范围闸落地:
条款写进 os-dev / pm-dispatch 三份文本的验证纪律章节。⛔ 不改任何测试套件、驱动代码或
其他 skill —— 存量拒收用例的回填是另一张单。

前提复核(先做的第一件事)

回读 PR #6142 正文,issue 引用的两组数字逐条对上,措辞也对得上:

实测 红的机制
driver-sql 28 例 红 22 / 绿 6(留绿的 6 个正好是全部对照块) 多数红在抛出 knex 的裸 Undefined binding(s) —— 一个 code / status 均为 undefined 的 Error,refusalOf 正常返回、只有信封断言失败
driver-turso remote 29 例 红 20 / 绿 9 20 个全部红在「本该拒收却编译出了 SQL」—— 该 transport 从不抛

#6142 原文的推论一字未改地成立:只断言「它抛了」的拒收用例,会在本 issue 所针对的
那个驱动上保持绿色
—— 未修的驱动本来就抛,只是抛的是没有信封的裸错。两种机制方向
相反,却是同一个洞:断言只有一位信息,而缺陷有两位。

三份文本的落点章节也逐一复核过,均在 origin/main(a560537)上原样存在,没有移动
或改名。

逐文件落点

1. .claude/agents/os-dev.md(英文,随该文件语种)

落点:Reverse verification — decide the expected direction BEFORE you run it.
三方向块之后Key-vs-value reachability criterion. 之前

理由:这条条款本身就是一次反向验证测出来的(#6142 预判方向再实测),而它的
下一条邻居 Key-vs-value 讲的正是「断言要对准规则真正判的那件事」。三段连起来是一条
递进:先决定该往哪个方向红 → 拒收类用例至少要能红 → 断言对准判据。落在别处会把这条
递进拆断。格式沿用该文件既有惯例(粗体首句 + 破折号 + 带实测数字的两条 bullet)。

新增(节选):

Rejection-class cases assert the envelope, not the throw. For any case whose
point is that bad input is refused, the minimum assertion set is the error's
code AND status (the ADR-0112 envelope). expect(...).toThrow() /
rejects.toThrow() on its own is not a rejection test: it carries one bit where
the defect has two, and PR #6142 (#6050) measured both ways it goes blind —
opposite directions, same hole:

  • A bare Error ⇒ permanently green. … A throw-only assertion therefore
    stays green on the very driver the issue targets.
  • A transport that never throws ⇒ red, but pointing away from the defect.
    it cannot separate "refused with the wrong envelope" from "did not refuse at
    all" — and those are exactly the two defects.

Where the wording is itself contract (#5240, one condition ⇒ one wording),
assert the message's first sentence on top of code+status, never instead
of them. A rejection test that cannot go red on a missing envelope reads as
coverage and is not.

2. .claude/skills/pm-dispatch/SKILL.md(中文,随该文件语种)—— 两处

该文件既有惯例是**「派发条款 + 复核条目」成对**:pin 翻转那条就是这个形状(step 5 两行

  • step 7 的 were the old position's pins flipped repo-wide?,后者显式回指
    step 5's two lines)。本条按同一形状落两处:

(a) step 5,多面组件…派发令的标准条款 之后、Issue 正文是线索,不是规格 之前。
沿用邻居那条的完整骨架:粗体首句以「—— 派发令的标准条款。」收尾 → 适用判据:
满足时派发令带这一句(原话): + 引文 → 实测出处。引文里带上范围闸,免得条款本身
诱发存量套件回填:

拒收类用例最低断言错误的 codestatus(ADR-0112 信封)。
expect(...).toThrow() / rejects.toThrow() 单独使用不构成拒收测试;措辞本身
是契约时(#5240「一个条件一种措辞」),首句断言加在 code+status 之上,
而不是代替它。本条约束你新写或改写的用例 —— 顺手回填存量套件不在本单范围内。

(b) step 7 复核清单,A ruling-implementation PR: were the old position's pins flipped repo-wide? 之后。 与它同族(两条都是「拒收断言还担不担载荷」),并按该清单
的惯例回指 step 5:

  • 拒收类用例的绿,是不是「它抛了」的绿? 判据:本单验收点含「应当被拒收」。抽查
    diff 里的拒收用例有没有断言 codestatus(ADR-0112 信封)…… 于是「28 例全绿」
    这种报告读起来是覆盖、实际证不了拒收。缺断言判 REWORK 补齐,而不是接受绿色输出。
    本条是 step 5 那条标准条款在复核侧的对账。

没有这条,step 7 那句 Test evidence … not a bare "tests pass" 只挡得住「没贴输出」,
挡不住「贴了真实输出、但那条断言根本红不了」—— 而 #6142 实测的恰恰是后者。

3. skills/objectstack-pm-dispatch/SKILL.md(英文,已发布)—— 两处

该文件是发布给第三方的泛化版,而且它自带一份内嵌 dev 模板 —— 这正是 frame sync
门禁说的「四份拷贝、三个文件」里它占两份的原因。既有验证纪律项在这份文件里就是两处
分布
的(Test evidence … 在 step 7 复核侧,capture REAL output 在内嵌模板的
Definition of done),本条照抄这个分布:

  • step 7 复核清单,紧跟 Test evidence … 之后:一条 PM 侧判据;
  • 内嵌 dev 模板,Definition of done 列表之后、When to STOP instead of coding.
    之前:一段独立段落(与该模板既有的 Practical trap when filing issues … 同为独立段
    的写法),因为模板是给没有任何上下文的 agent 单独粘贴用的,挂在 bullet 里份量不够。

#5451 route B(frame-sync 门禁头部记录的裁决)把这份拷贝刻意泛化:

  • 不带本仓 issue 号,机制照写、编号不写;
  • whatever fields your project's error envelope declares 代替 ADR-0112 的直接引用;
  • ⛔ 全文回避 role 一词 —— 该文件当前 role 出现次数为 0、且不在
    scripts/role-word-baseline.json 里,写进去会当场把 check:role-word 打红
    (ADR-0090 D3 棘轮:不在基线里的文件出现该词即红)。

⛔ 三轴决策框架:一字未动,改前改后各证一次

三份文件都携带 check:skill-frame-sync 钉住的三轴结构,本 PR 完全绕开它:

改前  ✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files
        3 axes: business-need → long-term-soundness → ai-authoring-safety
        binding sentence present in all 4; 4 count mention(s) agree; 40 markdown files scanned for undeclared copies.

改后  ✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files
        3 axes: business-need → long-term-soundness → ai-authoring-safety
        binding sentence present in all 4; 4 count mention(s) agree; 40 markdown files scanned for undeclared copies.

逐字相同,包括 4 count mention(s) agree —— 新条款一次也没有提到轴数或轴名(写作时
刻意回避了 axis / axes 词面,免得撞上 %N% / %Q% 的计数锚点)。

新门 check:skill-frame-freshness 也一并跑了(树是新鲜的,origin/main 现场 fetch):

改前  ✓ … the decision frame in this tree is current with origin/main (fetched just now).
        3/3 framework files are byte-identical to the ref (wording differences are not drift).

改后  ✓ … the decision frame in this tree is current with origin/main (fetched just now).
        HEAD is 3 commit(s) behind, but the frame itself is unchanged — that is fine.
        0/3 framework files are byte-identical to the ref (wording differences are not drift).

3/3 → 0/3预期内不参与判决:该门禁头部写明「STRUCTURE, NEVER BYTES ——
byte difference is reported as INFORMATION in the summary and never decides anything」。
三份文件都被本 PR 编辑过,字节自然不再相同;结构未变,所以判绿。落后的 3 个 commit
(#6205 / #6200 / #6201)与本 PR 的三个文件零交集,已核对改动文件清单。

门禁(全部本地实跑,文件已 git add 后再跑)

⚠️ 先 stage 再跑:check:role-word / check:nul-bytes 只扫 tracked 文件。

pnpm check:skill-frame-sync        → self-test 12 cases pass;4 copies isomorphic(改前/改后逐字相同)
pnpm check:skill-frame-freshness   → self-test 12 cases pass;frame current with origin/main
pnpm check:doc-authoring           → 365 files clean — no bare metadata literals
pnpm check:role-word               → OK (44 baselined file(s), no new occurrences)
pnpm check:nul-bytes               → OK (scanned 5925 tracked text file(s); no raw ASCII control bytes)
pnpm --filter @objectstack/spec check:skill-docs      → ✅ Skill docs in sync(README.md / skills-reference.mdx)
pnpm --filter @objectstack/spec check:skill-refs      → ✅ 9 generated files in sync with packages/spec
pnpm --filter @objectstack/spec check:skill-examples  → ✅ 208 prose examples type-check against @objectstack/spec

补两笔:

  • check:skill-examples 首跑红在 Build the spec first —— 新 worktree 缺 spec 构建产物,
    不是本改动(AGENTS.md §9 陈旧产物陷阱的镜像)。pnpm --filter @objectstack/spec build
    之后转绿,且构建后 git status 只有本 PR 的三个文件,无 tracked 产物漂移
    本 PR 未新增任何 os:check / ts 代码块(讨论纪律的条款不该带代码样例),该门本就
    与本 diff 无关,跑它只为留证。
  • check:skill-docs 绿说明 skills/README.md 索引不需要重生成 —— 索引取自
    frontmatter,而本 PR 只动正文、未动 frontmatter。

自查(超出门禁扫描面):grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' 扫三个文件,零命中。

为什么没有 changeset

.claude/ + skills/ 纯文本,不发布任何包、不改任何运行时行为 ⇒ 走 skip-changeset
标签,不写空 changeset(空 changeset 滞留发布,#4898)。标签已在 PR 建立后立即打上
并读回核对。


Generated by Claude Code

三份 os-dev / pm-dispatch 文本的验证纪律章节同步落条款,动机写死 #6142 实测的两种
失明机制(裸 Error 恒绿 / 从不抛的 transport 红得不指向缺陷)。

- .claude/agents/os-dev.md:反向验证块之后新增 "Rejection-class cases assert the
  envelope, not the throw"(dev 侧机制条款,英文,随该文件语种)。
- .claude/skills/pm-dispatch/SKILL.md:step 5 新增派发令标准条款(带适用判据 +
  原话引文),step 7 新增复核侧对账条目 —— 按该文件既有的「派发条款 + 复核条目」
  配对惯例(pin 翻转那条即此形)。
- skills/objectstack-pm-dispatch/SKILL.md:step 7 复核判据 + 内嵌 dev 模板的独立
  段落(两处,与该文件既有验证纪律项的分布一致);按 #5451 route B 泛化,不带本仓
  issue 号、不出现 role 一词。

⛔ 未触碰三轴决策框架:check:skill-frame-sync 改前改后均绿(4 copies / 3 axes /
binding sentence present in all 4)。
⛔ 未改动任何测试套件、驱动代码或其他 skill —— 存量拒收套件的回填是另一张单
(#6144 分诊的范围闸)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BDmDsu2575gDxeMCxXhDE3
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 7, 2026 11:24am

Request Review

@github-actions github-actions Bot added the size/s label Aug 7, 2026
@hotlong hotlong added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/s labels Aug 7, 2026 — with Claude
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Aug 7, 2026
@hotlong
hotlong marked this pull request as ready for review August 7, 2026 11:50
@hotlong
hotlong added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit 3e0f7e4 Aug 7, 2026
27 of 28 checks passed
@hotlong
hotlong deleted the claude/issue-6144-rejection-assert-envelope branch August 7, 2026 12:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[skill] 拒收类用例必须断言 code+status,不能只断言「它抛了」—— 否则在裸 Error 的驱动上恒绿(#6142 实测)

2 participants